package cn.demoncat.util.lang;

import cn.demoncat.util.lang.constant.StatusConstant;
import cn.demoncat.util.lang.constant.StringConstant;
import cn.demoncat.util.lang.entity.MapSo;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * 输入工具
 *
 * @author 延晓磊
 * @since 2021年01月14日
 */
public class InputUtil {

    // 最大输入长度
    public static final int MAX_SELECT_LENGTH = 1000;
    public static final int MAX_INPUT_TEXT_LENGTH = 100;
    public static final int MAX_INPUT_NUM_LENGTH = 18;
    // 限制参数
    public static final String LIMIT_LENGTH = "length";
    public static final String LIMIT_MIN = "min";
    public static final String LIMIT_MAX = "max";
    public static final String LIMIT_SEPARATOR = "separator";


    /**
     * 输入类型
     *
     * @author 延晓磊
     *
     * @since 2020年3月14日
     */
    public enum InputType{
        // 文本：默认值，长度
        INPUT_TEXT("10", "文本输入框"),
        // 数值：最大值，最小值，长度，分隔符；单位
        INPUT_NUM("20", "数值输入框"),
        INPUT_NUM_INT("21", "整数输入框"),
        INPUT_NUM2("22", "双数值输入框"),
        INPUT_NUM_INT2("23", "双整数输入框"),
        INPUT_NUM_UNIT("24", "数值单位组合框"),
        INPUT_NUM_INT_UNIT("25", "整数单位组合框"),
        // 选择框：可选项
        SELECT("30", "选择框"),
        DATE_TIME("40", "时间选择框"),
        DATE("41", "日期选择框"),
        AREA("50", "地址选择框"),
        AREA_PROVINCE("51", "省选择框"),
        UNIT("53", "单位选择框")
        ;

        private final String code;
        private final String text;

        InputType(String code, String text) {
            this.code = code;
            this.text = text;
        }

        public String getCode() {
            return code;
        }

        public String getText() {
            return text;
        }
    }

    /**
     * 输入限制（配置）
     *
     * @author 延晓磊
     * @since 2021年01月14日
     */
    public static class InputLimit{
        // 输入类型
        private String type;
        // 默认值,选项列表
        private String val;
        // 限制参数（配置的结果参数）
        private String valLimit;
        // 数值单位
        private String valUnit;
        // 是否筛选（选择框）
        private String search;
        // 是否必填
        private String required;

        // 长度
        private Integer length;
        // 最小值
        private Double min;
        // 最大值
        private Double max;
        // 分隔符： v1 - v2
        private String separator;
        // 最大选项数
        private int maxSelectLength = MAX_SELECT_LENGTH;
        // 最大文本输入长度
        private int maxInputTextLength = MAX_INPUT_TEXT_LENGTH;
        // 最大数值输入长度
        private int maxInputNumLength = MAX_INPUT_NUM_LENGTH;

        public InputLimit() {
            super();
        }

        /**
         * 反解析
         *
         * @param valLimit
         *
         * @author 延晓磊
         * @since 2021年01月14日
         */
        public InputLimit(String valLimit){
            if (StringUtils.isNotBlank(valLimit)) {
                JSONObject json = JsonUtil.parseObject(valLimit);
                if (json != null) {
                    this.length = json.getInteger(LIMIT_LENGTH);
                    this.min = json.getDouble(LIMIT_MIN);
                    this.max = json.getDouble(LIMIT_MAX);
                    this.separator = json.getString(LIMIT_SEPARATOR);
                }
            }
        }

        /**
         * 解析
         *
         * @param type
         * @param val
         * @param valUnit
         * @param search
         * @param required
         * @param length
         * @param min
         * @param max
         * @param separator
         *
         * @author 延晓磊
         * @since 2021年01月14日
         */
        public InputLimit(String type, String val, String valUnit, String search, String required, Integer length, Double min, Double max, String separator) {
            this.type = type;
            this.val = val;
            this.search = search;
            this.length = length;
            this.min = min;
            this.max = max;
            this.valUnit = valUnit;
            this.required = required;
            this.separator = separator;
        }

        /**
         * 参数校验-配置
         *
         * 成功：设置 limit.val, limit.valLimit, limit.valUnit, limit.search, limit.required
         * 失败：返回 err
         *
         * @return 错误消息
         *
         * @author 延晓磊
         * @since 2021年01月13日
         */
        public String vali(){
            // 是否必填
            if (StringUtils.isBlank(this.getRequired())) {
                this.setRequired(StatusConstant.Common.NO);
            }
            // 参数校验
            if (this.getType().equals(InputUtil.InputType.SELECT.getCode())) {
                // 选择框 - 可选项
                this.setVal(StringUtils.trimToEmpty(this.getVal()));
                if (StringUtils.isBlank(this.getVal())) {
                    return "选项列表不能为空";
                }
                if (this.getVal().length() > this.getMaxSelectLength()) {
                    return "选项列表录入太多";
                }
                List<String> values = new ArrayList<>();
                String value;
                for (String val : StringUtil.split(this.getVal())) {
                    value = StringUtils.trimToEmpty(val);
                    if (StringUtils.isBlank(value)) {
                        return "选择项不能为空";
                    }
                    values.add(value);
                }
                if (values.size() > new HashSet<>(values).size()) {
                    return "选择项不能重复";
                }
                // 可搜索
                if (StringUtils.isBlank(this.getSearch())) {
                    this.setSearch(StatusConstant.Common.NO);
                }
                // 整合限制
                this.setVal(StringUtil.join(values));
                this.setValLimit(StringConstant.EMPTY);
                this.setValUnit(StringConstant.EMPTY);
            }else {
                // 不可搜索
                this.setSearch(StatusConstant.Common.NO);
                // 类型处理
                if (this.getType().equals(InputUtil.InputType.INPUT_TEXT.getCode())) {
                    // 文本框 - 默认值，长度限制
                    this.setVal(StringUtils.trimToEmpty(this.getVal()));
                    if (this.getLength() == null) {
                        return "输入长度不能为空";
                    }
                    if (this.getLength() <= 0) {
                        return "输入长度必须是正数";
                    }
                    if (this.getLength() > this.getMaxInputTextLength()) {
                        return "输入长度不能大于" + this.getMaxInputTextLength();
                    }
                    if (this.getVal().length() > this.getLength()) {
                        return "默认值不能大于输入长度";
                    }
                    // 整合限制
                    this.setValLimit(JsonUtil.toStr(MapSo.getInstance(LIMIT_LENGTH, this.getLength())));
                    this.setValUnit(StringConstant.EMPTY);
                }else if (this.getType().startsWith("2")) {
                    // 数值框 - 长度限制
                    if (this.getLength() == null) {
                        return "输入长度不能为空";
                    }
                    if (this.getLength() <= 0) {
                        return "输入长度必须是正数";
                    }
                    if (this.getLength() > this.getMaxInputNumLength()) {
                        return "输入长度不能大于" + this.getMaxInputNumLength();
                    }
                    // 数值框 - 最大值，最小值限制
                    if (this.getMin() != null && this.getMax() != null && this.getMax() < this.getMin()) {
                        return "最大值不能小于最小值";
                    }
                    // 双数值框 - 分隔符
                    boolean isNum2 = ParamEqualsUtil.equals(this.getType(), InputUtil.InputType.INPUT_NUM2.getCode(), InputUtil.InputType.INPUT_NUM_INT2.getCode());
                    if (isNum2 && StringUtils.isBlank(this.getSeparator())) {
                        return "双数值的分隔符不能为空";
                    }
                    // 整合限制
                    MapSo map = MapSo.getInstance(LIMIT_LENGTH, this.getLength());
                    boolean isInt = ParamEqualsUtil.equals(this.getType(), InputUtil.InputType.INPUT_NUM_INT.getCode(), InputUtil.InputType.INPUT_NUM_INT2.getCode(), InputUtil.InputType.INPUT_NUM_INT_UNIT.getCode());
                    if (this.getMin() != null) {
                        map.put(LIMIT_MIN, isInt ? this.getMin().intValue() : this.getMin());
                    }
                    if (this.getMax() != null) {
                        map.put(LIMIT_MAX, isInt ? this.getMax().intValue() : this.getMax());
                    }
                    if (isNum2) {
                        map.put(LIMIT_SEPARATOR, this.getSeparator());
                    }
                    this.setVal(StringConstant.EMPTY);
                    if (ParamEqualsUtil.equals(this.getType(), InputType.INPUT_NUM_UNIT.getCode(), InputType.INPUT_NUM_INT_UNIT.getCode())) {
                        // 组合框的单位为用户自定义
                        this.setValUnit(StringConstant.EMPTY);
                    }else {
                        this.setValUnit(StringUtils.trimToEmpty(this.getValUnit()));
                    }
                    this.setValLimit(JsonUtil.toStr(map));
                }else{
                    // 其它类型
                    this.setVal(StringConstant.EMPTY);
                    this.setValLimit(StringConstant.EMPTY);
                    this.setValUnit(StringConstant.EMPTY);
                }
            }
            return null;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getVal() {
            return val;
        }

        public void setVal(String val) {
            this.val = val;
        }

        public Integer getLength() {
            return length;
        }

        public void setLength(Integer length) {
            this.length = length;
        }

        public Double getMin() {
            return min;
        }

        public void setMin(Double min) {
            this.min = min;
        }

        public Double getMax() {
            return max;
        }

        public void setMax(Double max) {
            this.max = max;
        }

        public int getMaxSelectLength() {
            return maxSelectLength;
        }

        public void setMaxSelectLength(int maxSelectLength) {
            this.maxSelectLength = maxSelectLength;
        }

        public int getMaxInputTextLength() {
            return maxInputTextLength;
        }

        public void setMaxInputTextLength(int maxInputTextLength) {
            this.maxInputTextLength = maxInputTextLength;
        }

        public int getMaxInputNumLength() {
            return maxInputNumLength;
        }

        public void setMaxInputNumLength(int maxInputNumLength) {
            this.maxInputNumLength = maxInputNumLength;
        }

        public String getValLimit() {
            return valLimit;
        }

        public void setValLimit(String valLimit) {
            this.valLimit = valLimit;
        }

        public String getValUnit() {
            return valUnit;
        }

        public void setValUnit(String valUnit) {
            this.valUnit = valUnit;
        }

        public String getSearch() {
            return search;
        }

        public void setSearch(String search) {
            this.search = search;
        }

        public String getRequired() {
            return required;
        }

        public void setRequired(String required) {
            this.required = required;
        }

        public String getSeparator() {
            return separator;
        }

        public void setSeparator(String separator) {
            this.separator = separator;
        }
    }

    /**
     * 输入数据（应用）
     *
     * @author 延晓磊
     * @since 2021年01月14日
     */
    public static class InputData{
        // 输入类型
        private String type;
        // 选项列表
        private String val;
        // 限制参数（配置时整合）
        private String valLimit;
        // 数值单位（限定 || 录入）
        private String valUnit;
        // 是否必填
        private String required;
        // 数据值
        private String value;

        // 长度
        private Integer length;
        // 最小值
        private Double min;
        // 最大值
        private Double max;
        // 分隔符： v1 - v2
        private String separator;

        public InputData() {
            super();
        }

        public InputData(String type, String val, String valLimit, String valUnit, String required, String value) {
            this.type = type;
            this.val = val;
            this.valLimit = valLimit;
            this.valUnit = valUnit;
            this.required = required;
            this.value = value;
            if (StringUtils.isNotBlank(this.valLimit)) {
                JSONObject json = JsonUtil.parseObject(this.valLimit);
                if (json != null) {
                    this.length = json.getInteger(LIMIT_LENGTH);
                    this.min = json.getDouble(LIMIT_MIN);
                    this.max = json.getDouble(LIMIT_MAX);
                    this.separator = json.getString(LIMIT_SEPARATOR);
                }
            }
        }

        /**
         * 校验录入值
         *
         * 成功：设置 data.val, data.valUnit
         * 失败：返回 err
         *
         * @param msg   消息前缀
         * @return 错误消息
         *
         * @author 延晓磊
         * @since 2021年01月15日
         */
        public String vali(String msg){
            this.setValue(StringUtils.trimToEmpty(this.getValue()));
            this.setValUnit(StringUtils.trimToEmpty(this.getValUnit()));
            // 必填校验
            if (StatusConstant.Common.isYes(this.getRequired())) {
                if (StringUtils.isBlank(this.getValue())) {
                    return msg + "不能为空";
                }
                if (StringUtils.isBlank(this.getValUnit()) && ParamEqualsUtil.equals(this.getType(), InputUtil.InputType.INPUT_NUM_UNIT.getCode(), InputUtil.InputType.INPUT_NUM_INT_UNIT.getCode())) {
                    return msg + "不能为空";
                }
            }
            // 输入校验
            if (this.getType().equals(InputUtil.InputType.SELECT.getCode())) {
                // 选择框 - 选项校验
                if (StringUtils.isNotBlank(this.getValue())) {
                    if (!StringUtil.splitToList(this.getVal()).contains(this.getValue())) {
                        return msg + "选项错误";
                    }
                }
                this.setValUnit(StringConstant.EMPTY);
            }else if(this.getType().equals(InputUtil.InputType.INPUT_TEXT.getCode())){
                // 文本框 - 输入长度
                if (this.getValue().length() > this.getLength()) {
                    return msg + "输入太长";
                }
                this.setValUnit(StringConstant.EMPTY);
            }else if(this.getType().equals(InputUtil.InputType.DATE_TIME.getCode())){
                // 时间 - 格式
                if (!this.getValue().isEmpty() && !LocalDateUtil.isDateTime(this.getValue())) {
                    return msg + "格式错误";
                }
                this.setValUnit(StringConstant.EMPTY);
            }else if(this.getType().equals(InputUtil.InputType.DATE.getCode())){
                // 日期 - 格式
                if (!this.getValue().isEmpty() && !LocalDateUtil.isDate(this.getValue())) {
                    return msg + "格式错误";
                }
                this.setValUnit(StringConstant.EMPTY);
            }else if(ParamEqualsUtil.equals(this.getType(), InputUtil.InputType.INPUT_NUM.getCode(), InputUtil.InputType.INPUT_NUM_UNIT.getCode())){
                // 数值 - 长度，格式，区间
                if (!this.getValue().isEmpty()) {
                    return valiNum(this.getValue(), msg, false);
                }
            }else if(ParamEqualsUtil.equals(this.getType(), InputUtil.InputType.INPUT_NUM_INT.getCode(), InputUtil.InputType.INPUT_NUM_INT_UNIT.getCode())){
                // 整数 - 长度，格式，区间
                if (!this.getValue().isEmpty()) {
                    return valiNum(this.getValue(), msg, true);
                }
            }else if(ParamEqualsUtil.equals(this.getType(), InputUtil.InputType.INPUT_NUM2.getCode(), InputUtil.InputType.INPUT_NUM_INT2.getCode())){
                // 整数 - 长度，格式，区间
                if (!this.getValue().isEmpty()) {
                    // 切割
                    String[] arr = StringUtils.split(StringUtils.deleteWhitespace(this.getValue()), this.getSeparator());
                    if (arr == null || arr.length != 2) {
                        return msg + "输入错误";
                    }
                    // 判断
                    String err;
                    if (this.getType().equals(InputUtil.InputType.INPUT_NUM2.getCode())) {
                        err = valiNum(arr[0], msg, false);
                        if (err == null) {
                            err = valiNum(arr[1], msg, false);
                        }
                    }else{
                        err = valiNum(arr[0], msg, true);
                        if (err == null) {
                            err = valiNum(arr[1], msg, true);
                        }
                    }
                    return err;
                }
            }else {
                // 其它
                this.setValUnit(StringConstant.EMPTY);
            }
            return null;
        }

        /**
         * 校验数值
         *
         * @param value
         * @return
         */
        private String valiNum(String value, String msg, boolean isInt){
            if (value.length() > this.getLength()) {
                return msg + "输入太长";
            }
            if (isInt) {
                if (!RegexpUtil.matcher(value,RegexpUtil.NumberRex.INTEGER)) {
                    return msg + "必须是数值";
                }
            }else{
                if (!RegexpUtil.matcher(value,RegexpUtil.NumberRex.ALL)) {
                    return msg + "必须是整数";
                }
            }
            double v = DoubleUtil.valueOf(value);
            if (this.getMin() != null && v < this.getMin()) {
                return msg + "不能小于" + this.getMin();
            }
            if (this.getMax() != null && v > this.getMax()) {
                return msg + "不能大于" + this.getMax();
            }
            return null;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getVal() {
            return val;
        }

        public void setVal(String val) {
            this.val = val;
        }

        public String getValLimit() {
            return valLimit;
        }

        public void setValLimit(String valLimit) {
            this.valLimit = valLimit;
        }

        public String getValUnit() {
            return valUnit;
        }

        public void setValUnit(String valUnit) {
            this.valUnit = valUnit;
        }

        public String getRequired() {
            return required;
        }

        public void setRequired(String required) {
            this.required = required;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }

        public Integer getLength() {
            return length;
        }

        public void setLength(Integer length) {
            this.length = length;
        }

        public Double getMin() {
            return min;
        }

        public void setMin(Double min) {
            this.min = min;
        }

        public Double getMax() {
            return max;
        }

        public void setMax(Double max) {
            this.max = max;
        }

        public String getSeparator() {
            return separator;
        }

        public void setSeparator(String separator) {
            this.separator = separator;
        }
    }

}
